home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / kernel / mknames.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-12  |  9.9 KB  |  418 lines  |  [TEXT/KAHL]

  1. /* Name generation for Xconq.
  2.    Copyright (C) 1991, 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. /* Naming is a special class of init method, since it may run both during
  10.    init and throughout a game.  Name generation has a very strong influence
  11.    on the flavor of a game, so it has some extra flexibility, including
  12.    a capability to use a simple context-free grammar to generate names. */
  13.  
  14. #include "conq.h"
  15.  
  16. extern Obj *make_namer PROTO ((Obj *sym, Obj *meth));
  17.  
  18. int totalsideweights;
  19.  
  20. void
  21. init_namers()
  22. {
  23.     totalsideweights = 0;
  24. }
  25.  
  26. /* The default side name list has the form
  27.    (((name "A") (noun "Aian")) ... ). */
  28.  
  29. void
  30. set_g_side_lib_default()
  31. {
  32.     int i;
  33.     Obj *tmp;
  34.     Obj *sidenamelist = lispnil, *sidenamelistend = lispnil;
  35.  
  36.     for (i = 0; i < MAXSIDES; ++i) {
  37.     sprintf(spbuf, "%c", 'A' + i);
  38.     sprintf(tmpbuf, "%cian", 'A' + i); /* should be in nlang.c? */
  39.     tmp = lispnil;
  40.     tmp = cons(cons(intern_symbol(keyword_name(K_NAME)),
  41.             cons(new_string(copy_string(spbuf)), lispnil)),
  42.            tmp);
  43.     tmp = cons(cons(intern_symbol(keyword_name(K_NOUN)),
  44.             cons(new_string(copy_string(tmpbuf)), lispnil)),
  45.            tmp);
  46.     tmp = cons(tmp, lispnil);
  47.     if (sidenamelist == lispnil) {
  48.         sidenamelist = tmp;
  49.     } else {
  50.         sidenamelistend->v.cons.cdr = tmp;
  51.     }
  52.     sidenamelistend = tmp;
  53.     }
  54.     set_g_side_lib(sidenamelist);
  55. }
  56.  
  57. /* Pick a side name that is not already being used. */
  58.  
  59. void
  60. make_up_side_name(side)
  61. Side *side;
  62. {
  63.     int uniq = FALSE, tries = 0, n, sofar;
  64.     Obj *sidelib, *subobj, *subelts, *lis, *filler;
  65.  
  66.     if ((sidelib = g_side_lib()) == lispnil) return;
  67.     /* (what if all weights were explicitly 0?) */
  68.     if (totalsideweights == 0) {
  69.     for (lis = sidelib; lis != lispnil; lis = cdr(lis)) {
  70.         totalsideweights +=
  71.           (numberp(car(car(lis))) ? c_number(car(car(lis))) : 1);
  72.     }
  73.     }
  74.     filler = lispnil;
  75.     while (tries++ < 100 * numsides) {
  76.     n = xrandom(totalsideweights);
  77.     sofar = 0;
  78.     subobj = lispnil;
  79.     for (lis = sidelib; lis != lispnil; lis = cdr(lis)) {
  80.         sofar += (numberp(car(car(lis))) ? c_number(car(car(lis))) : 1);
  81.         if (sofar > n) {
  82.         subobj = car(lis);
  83.         break;
  84.         }
  85.     }
  86.     /* (should scan and preprocess subobj before using it) */
  87.     /* Remove the weighting if present. */
  88.     if (numberp(car(subobj))) {
  89.         subobj = cdr(subobj);
  90.     }
  91.     uniq = TRUE;
  92.     for (subelts = subobj; subelts != lispnil; subelts = cdr(subelts)) {
  93.         if (stringp(cadr(car(subelts)))) {
  94.         if (name_in_use(side, c_string(cadr(car(subelts))))) {
  95.             uniq = FALSE;
  96.             break;
  97.         }
  98.         }
  99.     }
  100.     if (uniq) {
  101.         filler = subobj;
  102.         break;
  103.     }
  104.     }
  105.     /* Now fill the side from the chosen obj - no effect if it is nil. */
  106.     fill_in_side(side, filler, FALSE);
  107. }
  108.  
  109. /* This tests whether a given string is already being used by a side. */
  110.  
  111. int
  112. name_in_use(side, str)
  113. Side *side;
  114. char *str;
  115. {
  116.     Side *side2;
  117.  
  118.     if (empty_string(str))
  119.       return FALSE;
  120.     for_all_sides(side2) {
  121.     if (side2 != side) {
  122.         if ((side2->name && strcmp(str, side2->name) == 0)
  123.         || (side2->noun && strcmp(str, side2->noun) == 0)
  124.         || (side2->pluralnoun && strcmp(str, side2->pluralnoun) == 0)
  125.         || (side2->adjective && strcmp(str, side2->adjective) == 0)
  126.         ) return TRUE;
  127.     }
  128.     }
  129.     return FALSE;
  130. }
  131.  
  132. Obj *
  133. make_namer(sym, meth)
  134. Obj *sym, *meth;
  135. {
  136.     Obj *namer = new_pointer(sym, (char *) meth);
  137.  
  138.     return namer;
  139. }
  140.  
  141. /* Method to add names to units that want them and don't have them already. */
  142.  
  143. int
  144. name_units_randomly()
  145. {
  146.     Unit *unit;
  147.  
  148.     /* There's never any reason not to run this method. */
  149.     /* (should this announce progress?) */
  150.     for_all_units(unit) {
  151.     make_up_unit_name(unit);
  152.     assign_unit_number(unit);
  153.     }
  154.     return TRUE;
  155. }
  156.  
  157. /* Given a unit, return its naming method if it has one. */
  158.  
  159. char *
  160. unit_namer(unit)
  161. Unit *unit;
  162. {
  163.     Side *side;
  164.  
  165.     if (unit == NULL) return NULL;
  166.     /* Look for and return a side-specific namer if found. */
  167.     side = (unit->side ? unit->side : indepside);
  168.     if (side->unitnamers != NULL && side->unitnamers[unit->type] != NULL) {
  169.         return side->unitnamers[unit->type];
  170.     }
  171.     return u_namer(unit->type);
  172. }
  173.  
  174. /* Generate a name for a unit, using an appropriate method.
  175.  
  176.    It is possible (in fact encouraged) to add cool new unit name generation
  177.    methods in here, especially when the grammar-based or thematic methods
  178.    don't give the desired results. */
  179.  
  180. char *
  181. propose_unit_name(unit)
  182. Unit *unit;
  183. {
  184.     int u;
  185.     char *method;
  186.  
  187.     if (unit == NULL) return NULL;
  188.     u = unit->type;
  189.     method = unit_namer(unit);
  190.     if (method == NULL || strcmp(method, "") == 0) {
  191.     /* Nothing to work with. */
  192.     } else if (boundp(intern_symbol(method))) {
  193.     return run_namer(symbol_value(intern_symbol(method)));
  194.     } else {
  195.     /* Do builtin naming methods. */
  196.     switch (keyword_code(method)) {
  197.       case K_JUNKY:
  198.         /* Kind of a bizarre thing, but flavorful sometimes. */
  199.         if (unit->side) {
  200.         sprintf(spbuf, "%c%c-%s-%02d",
  201.             uppercase(unit->side->name[0]),
  202.             uppercase(unit->side->name[1]),
  203.             utype_name_n(u, 3), unit->number);
  204.         } else {
  205.         sprintf(spbuf, "%s-%d", utype_name_n(u, 3), unit->id);
  206.         }
  207.         return copy_string(spbuf);
  208.       default:
  209.         init_warning("No naming method `%s', ignoring", method);
  210.         break;
  211.     }
  212.     }
  213.     return NULL;
  214. }
  215.  
  216. /* This names only units that do not already have names. */
  217.  
  218. void
  219. make_up_unit_name(unit)
  220. Unit *unit;
  221. {
  222.     if (unit == NULL || unit->name != NULL) return;
  223.     /* (should check that proposed name is not in use by matching side and type?) */
  224.     unit->name = propose_unit_name(unit);
  225. }
  226.  
  227. /* Unit numbering only happens to designated types that are on a side. */
  228.  
  229. void
  230. assign_unit_number(unit)
  231. Unit *unit;
  232. {
  233.     if (u_assign_number(unit->type)
  234.     && unit->side != NULL
  235.     && unit->number == 0) {
  236.     /* Give it the next available number and increment. */
  237.     unit->number = (unit->side->counts)[unit->type]++;
  238.     } else {
  239.     /* Note that this will erase any already-assigned number,
  240.        if the type is one that is not supposed to be numbered. */
  241.     unit->number = 0;
  242.     }
  243. }
  244.  
  245. /* Given a naming method, run it and get back a string. */
  246.  
  247. char *
  248. run_namer(namer)
  249. Obj *namer;
  250. {
  251.     int len, ix;
  252.     Obj *prev;
  253.     Obj *rslt;
  254.     Obj *code = (Obj *) namer->v.ptr.data;
  255.     Obj *type;
  256.  
  257.     if (!consp(code))
  258.       return "?format?";
  259.     type = car(code);
  260.     if (!symbolp(type))
  261.       return "?type?";
  262.     switch (keyword_code(c_string(type))) {
  263.       case K_JUNKY:
  264.       case K_RANDOM:
  265.         len = length(cdr(code));
  266.     if (len > 0) {
  267.         ix = xrandom(len - 1) + 1;
  268.         prev = code;
  269.         while (--ix) prev = cdr(prev);
  270.         rslt = cadr(prev);
  271.         /* Splice out our desired name. */
  272.         prev->v.cons.cdr = cddr(prev);
  273.         return c_string(rslt);
  274.     } else {
  275.         return "?no more names?";
  276.     }
  277.     break;
  278.       case K_GRAMMAR:
  279.     return name_from_grammar(code);
  280.       default:
  281.     return "?method?";
  282.     }
  283. }
  284.  
  285. static int maxdepth;
  286.  
  287. char *
  288. name_from_grammar(grammar)
  289. Obj *grammar;
  290. {
  291.     char rslt[500];  /* not really safe... */
  292.     Obj *root = cadr(grammar);
  293.     Obj *depth = caddr(grammar);
  294.     Obj *rules = cdr(cddr(grammar));
  295.  
  296.     maxdepth = 5;
  297.     if (numberp(depth)) maxdepth = c_number(depth);
  298.     sprintf(rslt, "");
  299.     gen_name(root, rules, 0, rslt);
  300.     /* This should be optional maybe. */
  301.     rslt[0] = uppercase(rslt[0]);
  302.     return copy_string(rslt);
  303. }
  304.  
  305. /* Given a nonterminal and a set of rules, find and apply the right rule. */
  306.  
  307. void
  308. gen_name(nonterm, rules, depth, rslt)
  309. Obj *nonterm, *rules;
  310. int depth;
  311. char *rslt;
  312. {
  313.     Obj *lis;
  314.  
  315.     for (lis = rules; lis != lispnil; lis = cdr(lis)) {
  316.     if (equal(nonterm, car(car(lis)))) {
  317.         gen_from_rule(cadr(car(lis)), rules, depth, rslt);
  318.         return;
  319.     }
  320.     }
  321.     if (symbolp(nonterm)
  322.     && boundp(nonterm)
  323.     && pointerp(symbol_value(nonterm))) {
  324.     strcat(rslt, run_namer(symbol_value(nonterm)));
  325.     } else {
  326.     /* Assume that the purported nonterm symbol is actually a terminal. */
  327.     strcat(rslt, c_string(nonterm));
  328.     }
  329. }
  330.  
  331. /* Given a rule body, decide how to add to the output string.  This may
  332.    recurse, so there is a limit check. */
  333.  
  334. void
  335. gen_from_rule(rule, rules, depth, rslt)
  336. Obj *rule, *rules;
  337. int depth;
  338. char *rslt;
  339. {
  340.     Obj *lis;
  341.     int total, num, oldlen;
  342.     
  343.     if (depth >= maxdepth)
  344.       return;
  345.     switch (rule->type) {
  346.       case NUMBER:
  347.       case NIL:
  348.       case UTYPE:
  349.       case MTYPE:
  350.       case TTYPE:
  351.       case POINTER:
  352.       case EOFOBJ:
  353.     break;  /* ignore for now.. */
  354.       case SYMBOL:
  355.     gen_name(rule, rules, depth, rslt);
  356.     break;
  357.       case STRING:
  358.     strcat(rslt, c_string(rule));
  359.     break;
  360.       case CONS:
  361.         if (symbolp(car(rule))) {
  362.         switch (keyword_code(c_string(car(rule)))) {
  363.           case K_OR:
  364.         /* weighted selection */
  365.         total = 0;
  366.         for (lis = cdr(rule); lis != lispnil; lis = cdr(lis)) {
  367.             if (numberp(car(lis))) {
  368.             total += c_number(car(lis));
  369.             lis = cdr(lis);
  370.             } else {
  371.             total += 1;
  372.             }
  373.         }
  374.         /* We now know the range, make a random index into it. */
  375.         num = xrandom(total);
  376.         /* Go through again to figure out which choice the index
  377.            references. */
  378.         total = 0;
  379.         for (lis = cdr(rule); lis != lispnil; lis = cdr(lis)) {
  380.             if (numberp(car(lis))) {
  381.             total += c_number(car(lis));
  382.             lis = cdr(lis);
  383.             } else {
  384.             total += 1;
  385.             }
  386.             if (total > num) {
  387.             gen_from_rule(car(lis), rules, depth + 1, rslt);
  388.             return;
  389.             }
  390.         }
  391.         break;
  392.           case K_ANY:
  393.         /* uniform selection */
  394.         strcat(rslt, c_string(elt(cdr(rule),
  395.                       xrandom(length(cdr(rule))))));
  396.         break;
  397.           case K_CAPITALIZE:
  398.         oldlen = strlen(rslt);
  399.         gen_name(cadr(rule), rules, depth + 1, rslt);
  400.         if (islower(rslt[oldlen]))
  401.           rslt[oldlen] = uppercase(rslt[oldlen]);
  402.         break;
  403.           default:
  404.         /* Nested subsequence. */
  405.         for (lis = rule; lis != lispnil; lis = cdr(lis)) {
  406.             gen_from_rule(car(lis), rules, depth + 1, rslt);
  407.         }
  408.         }
  409.     } else {
  410.         /* syntax error */
  411.     }
  412.     break;
  413.       default:
  414.     case_panic("lisp type in grammar", rule->type);
  415.     break;
  416.     }
  417. }
  418.